// userapp.c
// single thread reference design

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "mpi.h"

#define N 100
#define NUM_THREADS 4
#define CHUNK N/NUM_THREADS

int main(int argc, char* argv[]) {

  int i;
  int *val, *new;

  int id, pn;
  int rc;
  MPI_Status st;
  MPI_Request req_s, req_r;
  char hostname[30];

  rc = MPI_Init(&argc, &argv);
  if (rc != MPI_SUCCESS) {
    printf ("Error starting MPI program. Terminating.\n");
    MPI_Abort(MPI_COMM_WORLD, rc);
  }

  MPI_Comm_size(MPI_COMM_WORLD, &pn);
  MPI_Comm_rank(MPI_COMM_WORLD, &id);
  gethostname(hostname, 30);
  printf ("[%s] %2d/%2d: [", hostname, id, pn);

  val = (int *)malloc(sizeof(int) * pn);
  new = (int *)malloc(sizeof(int) * pn);

  for (i=0; i<pn; i++) {
    val[i] = id;
    new[i] = 0;
  }

  for (i=0; i<pn; i++) printf("%X", val[i]);
  printf("] => [");

  MPI_Isend(val, pn, MPI_INT, id-1<0?pn-1:id-1, 0, MPI_COMM_WORLD, &req_s);
  MPI_Irecv(new, pn, MPI_INT, id+1<pn?id+1:0,   0, MPI_COMM_WORLD, &req_r);
  MPI_Wait(&req_s, &st);
  MPI_Wait(&req_r, &st);

  rc = MPI_Alltoall(val, 1, MPI_INT, new, 1, MPI_INT, MPI_COMM_WORLD);
  if (rc != MPI_SUCCESS) {
    printf ("Error calling MPI_Alltoall. Terminating.\n");
    free(val);
    free(new);
    MPI_Abort(MPI_COMM_WORLD, rc);
  }

  for (i=0; i<pn; i++) printf("%X", new[i]);
  printf("]\n");

  free(val);
  free(new);
  MPI_Finalize();

  return 0;
}
